/**
 ******************************************************************************
 * @file           : bsp_dma.c
 * @brief          : DMA Driver Implementation (STM32F4xx)
 ******************************************************************************
 * @attention
 *
 * DMA驱动实现文件
 * 提供简化的DMA内存传输功能
 * STM32F4xx使用DMA2 Stream0进行内存到内存传输
 *
 ******************************************************************************
 */

/* Includes ------------------------------------------------------------------*/
#include "./BSP/DMA/bsp_dma.h"
#include "./BSP/DMA/bsp_dma_config.h"
#include <string.h>

/* Private includes ----------------------------------------------------------*/
/* Private typedef -----------------------------------------------------------*/
/* Private define ------------------------------------------------------------*/
#define DMA_TIMEOUT_MS      1000    /*!< DMA传输超时时间 */

/* Private macro -------------------------------------------------------------*/
/* Private variables ---------------------------------------------------------*/
static DMA_HandleTypeDef sg_dma_memcpy_handle;
static volatile uint8_t sg_dma_transfer_done = 0;

/* Private function prototypes -----------------------------------------------*/
/* Private functions ---------------------------------------------------------*/
/* Exported functions --------------------------------------------------------*/

/**
 * @brief       DMA内存拷贝
 *
 * @param[out]  dst 目标地址
 * @param[in]   src 源地址
 * @param[in]   size 数据大小(字节)
 *
 * @retval      无
 *
 * @note        使用DMA2 Stream0进行内存到内存传输
 */
void dma_memcpy(void *dst, const void *src, uint32_t size)
{
    /* 小数据量直接用memcpy更快 */
    if (size < 32)
    {
        memcpy(dst, src, size);
        return;
    }

    /* 使能DMA2时钟 */
    __HAL_RCC_DMA2_CLK_ENABLE();

    /* 配置DMA */
    sg_dma_memcpy_handle.Instance = DMA2_Stream0;
    sg_dma_memcpy_handle.Init.Channel = DMA_CHANNEL_0;
    sg_dma_memcpy_handle.Init.Direction = DMA_MEMORY_TO_MEMORY;
    sg_dma_memcpy_handle.Init.PeriphInc = DMA_PINC_ENABLE;
    sg_dma_memcpy_handle.Init.MemInc = DMA_MINC_ENABLE;
    sg_dma_memcpy_handle.Init.PeriphDataAlignment = DMA_PDATAALIGN_WORD;
    sg_dma_memcpy_handle.Init.MemDataAlignment = DMA_MDATAALIGN_WORD;
    sg_dma_memcpy_handle.Init.Mode = DMA_NORMAL;
    sg_dma_memcpy_handle.Init.Priority = DMA_PRIORITY_HIGH;
    sg_dma_memcpy_handle.Init.FIFOMode = DMA_FIFOMODE_ENABLE;
    sg_dma_memcpy_handle.Init.FIFOThreshold = DMA_FIFO_THRESHOLD_FULL;
    sg_dma_memcpy_handle.Init.MemBurst = DMA_MBURST_SINGLE;
    sg_dma_memcpy_handle.Init.PeriphBurst = DMA_PBURST_SINGLE;

    /* 去初始化（清除之前的配置） */
    HAL_DMA_DeInit(&sg_dma_memcpy_handle);

    /* 初始化DMA */
    if (HAL_DMA_Init(&sg_dma_memcpy_handle) != HAL_OK)
    {
        log_e("DMA初始化失败");
        /* 降级使用memcpy */
        memcpy(dst, src, size);
        return;
    }

    /* 启动DMA传输 */
    sg_dma_transfer_done = 0;
    if (HAL_DMA_Start(&sg_dma_memcpy_handle, (uint32_t)src, (uint32_t)dst, size / 4) != HAL_OK)
    {
        log_e("DMA传输启动失败");
        memcpy(dst, src, size);
        return;
    }

    /* 等待传输完成（轮询方式） */
    uint32_t start_tick = HAL_GetTick();
    while (HAL_DMA_PollForTransfer(&sg_dma_memcpy_handle, HAL_DMA_FULL_TRANSFER, DMA_TIMEOUT_MS) != HAL_OK)
    {
        if (HAL_GetTick() - start_tick > DMA_TIMEOUT_MS)
        {
            log_e("DMA传输超时");
            HAL_DMA_Abort(&sg_dma_memcpy_handle);
            memcpy(dst, src, size);  /* 降级使用memcpy */
            return;
        }
    }

    log_d("DMA拷贝完成: %u字节", size);
}

/**
 * @brief       DMA内存填充
 *
 * @param[out]  dst 目标地址
 * @param[in]   value 填充值
 * @param[in]   size 数据大小(字节)
 *
 * @retval      无
 *
 * @note        使用软件方式，DMA不直接支持memset
 */
void dma_memset(void *dst, uint8_t value, uint32_t size)
{
    /* DMA不直接支持memset，使用标准库 */
    memset(dst, value, size);
}
